package com.meis.widget.rose;

import com.meis.widget.ResourceTable;
import com.meis.widget.utils.PointUtils;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.surfaceprovider.SurfaceProvider;
import ohos.agp.graphics.SurfaceOps;
import ohos.agp.render.BlendMode;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.global.resource.ResourceManager;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.ImageInfo;
import ohos.media.image.common.Size;

import java.io.IOException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * Created by wenshi on 2018/6/26.
 * Description 改变自'那夕阳下的奔跑，是我逝去的青春'
 * 'https://github.com/jenly1314/GiftSurfaceView'
 */
public class RoseGiftSurfaceView extends SurfaceProvider implements SurfaceOps.Callback {

    private static final String ASSET_LOVE = "resources/rawfile/love.json";
    private static final int EMPTY_FLAG = 1;
    //不使用时回收c层内存
    private PixelMap bitmap;
    //画布
    // private Canvas canvas;
    //画笔
    private Paint paint;
    //随机数
    private SecureRandom random;
    //起点集合
    private Point[] startPoint;
    //终点集合
    private Point[] endPoint;
    //动画期间点集合
    private Point[] currentPoint;
    //点集合
    private List<Point> points = new ArrayList<>();
    //控件宽度
    private float width;
    //控件高度
    private float height;
    /**
     * 缩放比例
     */
    private float scale = 1;
    /**
     * 偏移（非随机情况下有效）
     */
    private int offsetX;
    //y轴偏移量
    private int offsetY;
    //属性动画
    private AnimatorValue animator;
    private int duration = 3000;
    private SurfaceOps holder;
    private PixelMapHolder holder1;

    public RoseGiftSurfaceView(Context context) {
        this(context, null);
    }

    public RoseGiftSurfaceView(Context context, AttrSet attrs) {
        this(context, attrs, "");
    }

    public RoseGiftSurfaceView(Context context, AttrSet attrs, String defName) {
        super(context, attrs, defName);
        initData();
        addDrawTask((component, canvas) -> drawBitmap(canvas));
    }

    //初始化数据
    private void initData() {
        paint = new Paint();
        //paint抗锯齿
        paint.setAntiAlias(true);

        random = new SecureRandom();

        //设置透明
        pinToZTop(true);

        holder = getSurfaceOps().orElse(null);

        if (holder != null) {
            holder.setFormat(-3);
            holder.addCallback(this);
        }

        try {
            //设置图片
            setImageBitmap(getBitmapByResource(ResourceTable.Media_mei_rose), 0.7F);
        } catch (Exception e) {
            e.printStackTrace();
        }

        //设置点集合 性能待优化
        try {
            points = PointUtils.getListPointByResourceJson(RoseGiftSurfaceView.this.getContext(), ASSET_LOVE);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * getBitmapByResource
     *
     * @param resId 资源Id
     * @return PixelMap
     * @throws Exception Exception
     */
    private PixelMap getBitmapByResource(int resId) throws Exception {
        ResourceManager manager = getContext().getResourceManager();
        return ImageSource.create(manager.getResource(resId), null).createPixelmap(null);
    }

    /**
     * 设置图片资源
     *
     * @param resId 资源Id
     */
    public void setImageResource(int resId) {
        try {
            setImageBitmap(getBitmapByResource(resId));
        } catch (Exception ignored) {
        }
    }

    /**
     * 设置图片资源
     *
     * @param resId 资源Id
     * @param scale 缩放（图片）
     */
    public void setImageResource(int resId, float scale) {
        try {
            setImageBitmap(getBitmapByResource(resId), scale);
        } catch (Exception ignored) {
        }
    }

    /**
     * 设置ImageBitmap
     *
     * @param bitmap PixelMap
     */
    public void setImageBitmap(PixelMap bitmap) {
        this.bitmap = bitmap;
    }

    /**
     * 设置ImageBitmap
     *
     * @param bitmap PixelMap
     * @param scale  缩放（图片）
     */
    public void setImageBitmap(PixelMap bitmap, float scale) {
        this.bitmap = scaleBitmap(bitmap, scale);
        holder1 = new PixelMapHolder(bitmap);
    }

    /**
     * 图片按比例缩放
     *
     * @param bmp   PixelMap
     * @param scale 缩放（图片）
     * @return PixelMap
     */
    private PixelMap scaleBitmap(PixelMap bmp, float scale) {
        ImageInfo info = bmp.getImageInfo();
        int width = (int) (info.size.width * scale);
        int height = (int) (info.size.height * scale);
        PixelMap.InitializationOptions opts = new PixelMap.InitializationOptions();
        opts.size = new Size(width, height);
        opts.alphaType = info.alphaType;
        return PixelMap.create(bmp, opts);
    }

    /**
     * 拼图的终点坐标的整体缩放
     *
     * @param scale   点坐标整体缩放
     *                1.0f 表示坐标
     * @param offsetX X 轴偏移量
     * @param offsetY Y 轴偏移量
     */
    public void setPointScale(float scale, int offsetX, int offsetY) {
        this.scale = scale;
        this.offsetX = offsetX;
        this.offsetY = offsetY;
    }

    public void setListPoint(List<Point> points) {
        this.points = points;
    }

    /**
     * 更新点集合
     *
     * @param points List<Point> points
     */
    private void updatePoints(List<Point> points) {
        if (verifyListEmpty(points)) return;
        int number = points.size();
        startPoint = new Point[number];
        endPoint = new Point[number];
        currentPoint = new Point[number];
        for (int i = 0; i < number; i++) {
            endPoint[i] = new Point((int) (points.get(i).getPointX() * scale) + offsetX,
                    (int) ((points.get(i).getPointY() + offsetY) * scale));
            startPoint[i] = random((int) endPoint[i].getPointX(), (int) endPoint[i].getPointY());
            currentPoint[i] = new Point(0, 0);
        }
    }

    /**
     * 随机一个点
     *
     * @param endX endX
     * @param endY endY
     * @return Point
     */
    private Point random(int endX, int endY) {
        int x = 0;
        int y = 0;
        Size size = bitmap.getImageInfo().size;
        if (width >= size.width && height >= size.width) {
            x = random.nextInt((int) (width - size.width * 2)) + size.width;
            y = random.nextInt((int) (height - size.height * 2)) + size.height;
        }
        if (x == endX && y == endY) {
            random(endX, endY);
        }
        return new Point(x, y);
    }

    private boolean verifyListEmpty(Collection<Point> array) {
        if (array == null || array.isEmpty()) {
            return true;
        }
        return false;
    }

    public void updateGiftSurfaceViewParams(int width, int height) {
        this.width = width;
        this.height = height;
        if (width == 0 || height == 0) {
            Display display = DisplayManager.getInstance().getDefaultDisplay(getContext()).get();
            this.width = display.getRealAttributes().width;
            this.height = display.getRealAttributes().height;
        }
        setPointScale(1, (int) (this.width / 10), (int) (this.height / 3.8f));
        updatePoints(points);
    }

    public void startAnimation() {
        if (animator != null && animator.isRunning()) {
            return;
        }
        animator = new AnimatorValue();
        animator.setDuration(duration);
        animator.setLoopedCount(0);
        animator.setValueUpdateListener((animatorValue, v) -> {
            getCurrentPoint(v);
            invalidate();
        });
        animator.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
            }

            @Override
            public void onStop(Animator animator) {
            }

            @Override
            public void onCancel(Animator animator) {
            }

            @Override
            public void onEnd(Animator animator) {
                try {
                    Thread.sleep(300);
                } catch (InterruptedException ignored) {

                }
                animator.start();
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }
        });
        animator.start();
    }

    public void stopAnimation() {
        holder.removeCallback(this);
        if (animator != null && animator.isRunning()) {
            animator.cancel();
        }
    }

    private void getCurrentPoint(float valueAnimator) {
        if (startPoint == null || endPoint == null) return;
        for (int i = 0; i < startPoint.length; i++) {
            float tempX = startPoint[i].getPointX() - (int) ((startPoint[i].getPointX() - endPoint[i].getPointX()) * valueAnimator);
            float tempY = startPoint[i].getPointY() - (int) ((startPoint[i].getPointY() - endPoint[i].getPointY()) * valueAnimator);
            currentPoint[i].modify(tempX, tempY);
        }
    }

    private void drawBitmap(Canvas canvas) {
        canvas.drawColor(Color.WHITE.getValue(), BlendMode.COLOR);
        if (currentPoint != null) {
            Size size = bitmap.getImageInfo().size;
            for (Point point : currentPoint) {
                canvas.drawPixelMapHolder(holder1,
                        (float) ((double) point.getPointX() - size.width * .5d),
                        (float) ((double) point.getPointY() - size.height * .5d),
                        paint);
            }
        }

    }

    public int getDuration() {
        return duration;
    }

    public void setDuration(int duration) {
        this.duration = duration;
    }

    @Override
    public void surfaceCreated(SurfaceOps surfaceOps) {

    }

    @Override
    public void surfaceChanged(SurfaceOps surfaceOps, int format, int width, int height) {
        updateGiftSurfaceViewParams(width, height);
    }

    @Override
    public void surfaceDestroyed(SurfaceOps surfaceOps) {

    }
}
